﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using XNAGameEngine.GameState;
using XNAGameEngine.Logs;

namespace XNAGameEngine.Input
{
    /// <summary>
    /// Manager to handle the registration and detection of mouse events such as mousing over
    /// and clicking on objects. In order to register objects as listeners to these events
    /// the object must implement the IMouseEventCallbacks interface.
    /// </summary>
    public class ManagerMouseEvents : GameComponent, IManagerMouseEvents, IInputListener
    {
        #region local fields

        private Vector2 _mousePosition;
        private MouseClick _mouseAction;
        private bool _mouseEvent;

        /// <summary>
        /// List of objects that are currently moused over
        /// </summary>
        private List<IMouseEventListener> _mousedOverList;

        /// <summary>
        /// List of all the objects listening
        /// </summary>
        private List<IMouseEventListener> _notMousedOverList;

        /// <summary>
        /// Temporary list used for moving objects between lists
        /// </summary>
        private List<IMouseEventListener> _tempList;

        #endregion

        #region constructors

        /// <summary>
        /// Create a new ManagerMouseEvents instance
        /// </summary>
        /// <param name="game">reference to the game</param>
        /// <param name="inputManager">reference to the input manager to receiving mouse input events</param>
        public ManagerMouseEvents(Game game, IManagerInput inputManager)
            : base(game)
        {
            inputManager.ToggleInputListening(true, this);
            Game.Services.AddService(typeof(IManagerMouseEvents), this);
            Game.Components.Add(this);

            _mousedOverList = new List<IMouseEventListener>();
            _notMousedOverList = new List<IMouseEventListener>();
            _tempList = new List<IMouseEventListener>();
            ManagerLogs.Write("ManagerMouseEvents: Created");
        }

        #endregion

        #region local methods

        public override void Update(GameTime gameTime)
        {
            foreach (IMouseEventListener listener in _mousedOverList)
            {
                if (!listener.BoundingBox.Contains((int)_mousePosition.X, (int)_mousePosition.Y))
                {
                    _notMousedOverList.Add(listener);
                    _tempList.Add(listener);
                    listener.OnMouseExit();
                }
            }

            foreach (IMouseEventListener listener in _tempList)
                _mousedOverList.Remove(listener);
            _tempList.Clear();

            foreach (IMouseEventListener listener in _notMousedOverList)
            {
                if (listener.BoundingBox.Contains((int)_mousePosition.X, (int)_mousePosition.Y))
                {
                    _mousedOverList.Add(listener);
                    _tempList.Add(listener);
                    listener.OnMouseEnter();
                }
            }

            foreach (IMouseEventListener listener in _tempList)
                _notMousedOverList.Remove(listener);
            _tempList.Clear();

            if (!_mouseEvent || !GameEngine.GetService<IManagerGameState>().IsCurrentStateActive)
                return;

            if (_mouseAction != MouseClick.MOVE)
            {
                foreach (IMouseEventListener listener in _mousedOverList)
                    listener.OnMouseClick(_mouseAction);
                _mouseEvent = false;
                return;
            }



            _mouseEvent = false;
            base.Update(gameTime);
        }

        #endregion

        #region IManagerMouseEvents Members

        /// <summary>
        /// <para>Register or unregister an object from receiving mouse event notifications.</para>
        /// <para>The object passed in must implement the interface IMouseEventCallbacks or
        /// an exception will be thrown</para>
        /// </summary>
        /// <param name="listeningState">true to register the object, false to unregister</param>
        /// <param name="listener">the object to register or unregister</param>
        public void ToggleMouseEventListener(bool listeningState, object listener)
        {
            if (!(listener is IMouseEventListener))
                throw new SystemException("listener does not implement interface IMouseEventCallbacks");

            IMouseEventListener temp = listener as IMouseEventListener;

            if (listeningState)
            {
                _notMousedOverList.Add(temp);
                return;
            }

            _mousedOverList.Remove(temp);
            _notMousedOverList.Remove(temp);
        }

        #endregion

        #region IInputCallbacks Members

        /// <summary>
        /// Receives input events and handles the calling of mouse events when neceesary
        /// </summary>
        /// <param name="input">the input</param>
        public void InputPressed(InputMessage input)
        {
            if (input._mouse == null)
                return;

            _mousePosition = input._vectorVal.Value;
            _mouseAction = input._mouse.Value;
            _mouseEvent = true;
        }

        /// <summary>
        /// Not implemented. You may add implementation here if it is necessary
        /// </summary>
        /// <param name="input">the input</param>
        public void InputReleased(InputMessage input)
        { }

        /// <summary>
        /// Not implemented. You may add implementation here if it is necessary
        /// </summary>
        /// <param name="input">the input</param>
        public void InputHeld(InputMessage input)
        {
        }

        /// <summary>
        /// Throw this event to unregister this object once it has already been registered.
        /// </summary>
        public event ManagerInput.InputRegisterListenerEventHandler ThrowUnregisterListenerEvent;

        #endregion
    }
}
